﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using AnthStat.Domain;

namespace AnthStat.WPF.Applications
{
    public sealed class MeasurementViewModel : ViewModelBase
    {
        #region Members
        private Measurement _measurement;
        private bool _isEditing = false;
        private bool _isShowingIndividualChart = false;
        private object _syncLock = new object();
        #endregion // Members

        #region Events
        #endregion // Events

        #region Constructors
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="measurement">The domain model measurement object that this view model represents</param>
        public MeasurementViewModel(Measurement measurement)
        {
            // pre
            Contract.Requires(measurement != null);

            // post
            Contract.Ensures(_measurement != null);

            _measurement = measurement;
            _measurement.ScoresUpdated += _measurement_ScoresUpdated;
        }
        #endregion // Constructors

        #region Properties

        /// <summary>
        /// The global record ID for this visit record
        /// </summary>
        public string GlobalRecordId
        {
            get { return _measurement.GlobalRecordId; }
        }

        public bool IsShowingIndividualChart
        {
            get
            {
                return _isShowingIndividualChart;
            }
            set
            {
                if (_isShowingIndividualChart != value)
                {
                    _isShowingIndividualChart = value;
                    RaisePropertyChanged("IsShowingIndividualChart");
                }
            }
        }

        public bool IsEditing
        {
            get
            {
                return _isEditing;
            }
            set
            {
                if (_isEditing != value)
                {
                    _isEditing = value;
                    RaisePropertyChanged("IsEditing");
                }
            }
        }

        [Required]
        public bool IsRecumbent
        {
            get
            {
                return _measurement.IsRecumbent;
            }
            set
            {
                if (_measurement.IsRecumbent != value)
                {
                    _measurement.IsRecumbent = value;
                    RaisePropertyChanged("IsRecumbent");
                }
            }
        }

        [Required]
        public bool HasOedema
        {
            get
            {
                return _measurement.HasOedema;
            }
            set
            {
                if (_measurement.HasOedema != value)
                {
                    _measurement.HasOedema = value;
                    RaisePropertyChanged("HasOedema");
                }
            }
        }

        public string Notes
        {
            get
            {
                return _measurement.Notes;
            }
            set
            {
                if (_measurement.Notes != value)
                {
                    _measurement.Notes = value;
                    RaisePropertyChanged("Notes");
                }
            }
        }

        public DateTime DateMeasurement
        {
            get
            {
                return _measurement.DateMeasurement;
            }
            set
            {
                if (_measurement.DateMeasurement != value)
                {
                    _measurement.DateMeasurement = value;
                    RaisePropertyChanged("DateMeasurement");
                    RaisePropertyChanged("AgeDays");
                    RaisePropertyChanged("AgeMonths");
                    RaisePropertyChanged("AgeYears");
                }
            }
        }

        public double AgeDays
        {
            get
            {
                return _measurement.AgeDays;
            }
            set
            {
                if (!_measurement.AgeDays.Equals(value))
                {
                    _measurement.AgeDays = value;
                    RaisePropertyChanged("AgeDays");
                    RaisePropertyChanged("AgeMonths");
                    RaisePropertyChanged("AgeYears");
                }
            }
        }

        public double AgeMonths
        {
            get
            {
                return _measurement.AgeMonths;
            }
        }

        public double AgeYears
        {
            get
            {
                return (_measurement.AgeMonths / 12.0);
            }
        }

        [CustomValidation(typeof(MeasurementViewModel), "HeightValidate")]
        public double? Height
        {
            get
            {
                return _measurement.Height;
            }
            set
            {
                if (_measurement.Height != value)
                {
                    _measurement.Height = value;
                    RaisePropertyChanged("Height");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        [CustomValidation(typeof(MeasurementViewModel), "WeightValidate")]
        public double? Weight
        {
            get
            {
                return _measurement.Weight;
            }
            set
            {
                if (_measurement.Weight != value)
                {
                    _measurement.Weight = value;
                    RaisePropertyChanged("Weight");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        public double? BodyMassIndex
        {
            get
            {
                return _measurement.BodyMassIndex;
            }
            //set
            //{
            //    if (_measurement.BodyMassIndex != value)
            //    {
            //        _measurement.BodyMassIndex = value;
            //        RaisePropertyChanged("BodyMassIndex");
            //    }
            //}
        }

        [CustomValidation(typeof(MeasurementViewModel), "ArmCircumferenceValidate")]
        public double? MidUpperArmCircumference
        {
            get
            {
                return _measurement.MidUpperArmCircumference;
            }
            set
            {
                if (_measurement.MidUpperArmCircumference != value)
                {
                    _measurement.MidUpperArmCircumference = value;
                    RaisePropertyChanged("MidUpperArmCircumference");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        [CustomValidation(typeof(MeasurementViewModel), "SubscapularSkinfoldValidate")]
        public double? SubscapularSkinfold
        {
            get
            {
                return _measurement.SubscapularSkinfold;
            }
            set
            {
                if (_measurement.SubscapularSkinfold != value)
                {
                    _measurement.SubscapularSkinfold = value;
                    RaisePropertyChanged("SubscapularSkinfold");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        [CustomValidation(typeof(MeasurementViewModel), "TricepsSkinfoldValidate")]
        public double? TricepsSkinfold
        {
            get
            {
                return _measurement.TricepsSkinfold;
            }
            set
            {
                if (_measurement.TricepsSkinfold != value)
                {
                    _measurement.TricepsSkinfold = value;
                    RaisePropertyChanged("TricepsSkinfold");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        [CustomValidation(typeof(MeasurementViewModel), "HeadCircumferenceValidate")]
        public double? HeadCircumference
        {
            get
            {
                return _measurement.HeadCircumference;
            }
            set
            {
                if (_measurement.HeadCircumference != value)
                {
                    _measurement.HeadCircumference = value;
                    RaisePropertyChanged("HeadCircumference");

                    if (IsEditing && CalculateIndicatorsCommand.CanExecute(null))
                    {
                        CalculateIndicatorsCommand.Execute(null);
                    }
                }
            }
        }

        public static ValidationResult HeadCircumferenceValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.HeadCircumference < 25 || m.HeadCircumference > 64)
                {
                    return new ValidationResult("Head circumference must be between 25 and 64 centimeters.",
                        new List<string> { "HeadCircumference" });
                }
            }

            return ValidationResult.Success;
        }

        public static ValidationResult TricepsSkinfoldValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.TricepsSkinfold < 1.8 || m.TricepsSkinfold > 40)
                {
                    return new ValidationResult("Triceps skinfold must be between 1.8 and 40 millimeters.",
                        new List<string> { "TricepsSkinfold" });
                }
            }

            return ValidationResult.Success;
        }

        public static ValidationResult SubscapularSkinfoldValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.SubscapularSkinfold < 1.8 || m.SubscapularSkinfold > 40)
                {
                    return new ValidationResult("Subscapular skinfold must be between 1.8 and 40 millimeters.",
                        new List<string> { "SubscapularSkinfold" });
                }
            }

            return ValidationResult.Success;
        }

        public static ValidationResult ArmCircumferenceValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.MidUpperArmCircumference < 6 || m.MidUpperArmCircumference > 35)
                {
                    return new ValidationResult("Mid upper arm circumference must be between 6 and 35 centimeters.",
                        new List<string> { "MidUpperArmCircumference" });
                }
            }

            return ValidationResult.Success;
        }

        public static ValidationResult HeightValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.Height < 38 || m.Height > 150)
                {
                    return new ValidationResult("Height/length must be between 38 and 150 centimeters.",
                        new List<string> { "Height" });
                }
            }
            else if (m.AgeMonths > 60)
            {
                if (m.Height < 38 || m.Height > 230)
                {
                    return new ValidationResult("Height/length must be between 38 and 230 centimeters.",
                        new List<string> { "Height" });
                }
            }

            return ValidationResult.Success;
        }

        public static ValidationResult WeightValidate(object obj, ValidationContext context)
        {
            var m = (MeasurementViewModel)context.ObjectInstance;

            if (m.AgeMonths <= 60)
            {
                if (m.Weight < 0.9 || m.Weight > 58)
                {
                    return new ValidationResult("Weight must be between 0.9 and 58 kilograms.",
                        new List<string> { "Weight" });
                }
            }
            else if (m.AgeMonths > 60)
            {
                if (m.Weight < 0.9 || m.Weight > 275)
                {
                    return new ValidationResult("Weight must be between 0.9 and 275 kilograms.",
                        new List<string> { "Weight" });
                }
            }

            return ValidationResult.Success;
        }


        public double? CDCHAZ
        {
            get
            {
                return _measurement.ResultsCDC.StatureForAgeZ;
            }
        }

        public double? CDCHAP
        {
            get
            {
                return _measurement.ResultsCDC.StatureForAgeP;
            }
        }

        public double? CDCWAZ
        {
            get
            {
                return _measurement.ResultsCDC.WeightForAgeZ;
            }
        }

        public double? CDCWAP
        {
            get
            {
                return _measurement.ResultsCDC.WeightForAgeP;
            }
        }

        public double? CDCWHZ
        {
            get
            {
                return _measurement.ResultsCDC.WeightForStatureZ;
            }
        }

        public double? CDCWHP
        {
            get
            {
                return _measurement.ResultsCDC.WeightForStatureP;
            }
        }

        public double? CDCBMIZ
        {
            get
            {
                return _measurement.ResultsCDC.BMIForAgeZ;
            }
        }

        public double? CDCBMIP
        {
            get
            {
                return _measurement.ResultsCDC.BMIForAgeP;
            }
        }

        public double? CDCHCZ
        {
            get
            {
                return _measurement.ResultsCDC.HeadCircumferenceForAgeZ;
            }
        }

        public double? CDCHCP
        {
            get
            {
                return _measurement.ResultsCDC.HeadCircumferenceForAgeP;
            }
        }


        public double? WHOHAZ
        {
            get
            {
                return _measurement.ResultsWHO.StatureForAgeZ;
            }
        }

        public double? WHOHAP
        {
            get
            {
                return _measurement.ResultsWHO.StatureForAgeP;
            }
        }

        public double? WHOWAZ
        {
            get
            {
                return _measurement.ResultsWHO.WeightForAgeZ;
            }
        }

        public double? WHOWAP
        {
            get
            {
                return _measurement.ResultsWHO.WeightForAgeP;
            }
        }

        public double? WHOWHZ
        {
            get
            {
                return _measurement.ResultsWHO.WeightForStatureZ;
            }
        }

        public double? WHOWHP
        {
            get
            {
                return _measurement.ResultsWHO.WeightForStatureP;
            }
        }

        public double? WHOBMIZ
        {
            get
            {
                return _measurement.ResultsWHO.BMIForAgeZ;
            }
        }

        public double? WHOBMIP
        {
            get
            {
                return _measurement.ResultsWHO.BMIForAgeP;
            }
        }

        public double? WHOHCZ
        {
            get
            {
                return _measurement.ResultsWHO.HeadCircumferenceForAgeZ;
            }
        }

        public double? WHOHCP
        {
            get
            {
                return _measurement.ResultsWHO.HeadCircumferenceForAgeP;
            }
        }

        public double? WHOSSFZ
        {
            get
            {
                return _measurement.ResultsWHO.SSFForAgeZ;
            }
        }

        public double? WHOSSFP
        {
            get
            {
                return _measurement.ResultsWHO.SSFForAgeP;
            }
        }

        public double? WHOTSFZ
        {
            get
            {
                return _measurement.ResultsWHO.TSFForAgeZ;
            }
        }

        public double? WHOTSFP
        {
            get
            {
                return _measurement.ResultsWHO.TSFForAgeP;
            }
        }

        public double? WHOMUACZ
        {
            get
            {
                return _measurement.ResultsWHO.MidUpperArmCircumferenceForAgeZ;
            }
        }

        public double? WHOMUACP
        {
            get
            {
                return _measurement.ResultsWHO.MidUpperArmCircumferenceForAgeP;
            }
        }
        #endregion // Properties

        #region Commands
        public ICommand CalculateIndicatorsCommand { get { return new RelayCommand(CalculateIndicatorsCommandExecute, CanExecuteCalculateIndicatorsCommand); } }
        private void CalculateIndicatorsCommandExecute()
        {
            _measurement.CalculateBodyMassIndex();
            _measurement.ResultsCDC.CalculateScores();
            _measurement.ResultsWHO.CalculateScores();
        }

        private bool CanExecuteCalculateIndicatorsCommand()
        {
            return true;
        }

        public ICommand ToggleShowIndividualChartCommand { get { return new RelayCommand(ToggleShowIndividualChartCommandExecute, CanExecuteToggleShowIndividualChartCommand); } }
        private void ToggleShowIndividualChartCommandExecute()
        {
            IsShowingIndividualChart = !IsShowingIndividualChart;
        }

        private bool CanExecuteToggleShowIndividualChartCommand()
        {
            return true;
        }
        #endregion // Commands

        #region Methods

        private void _measurement_ScoresUpdated(object sender, EventArgs e)
        {
            RaisePropertyChanged("AgeDays");
            RaisePropertyChanged("AgeMonths");
            RaisePropertyChanged("AgeYears");

            RaisePropertyChanged("CDCBMIP");
            RaisePropertyChanged("CDCBMIZ");
            RaisePropertyChanged("CDCHAP");
            RaisePropertyChanged("CDCHAZ");
            RaisePropertyChanged("CDCWAP");
            RaisePropertyChanged("CDCWAZ");
            RaisePropertyChanged("CDCWHP");
            RaisePropertyChanged("CDCWHZ");
            RaisePropertyChanged("CDCHCP");
            RaisePropertyChanged("CDCHCZ");

            RaisePropertyChanged("WHOBMIP");
            RaisePropertyChanged("WHOBMIZ");
            RaisePropertyChanged("WHOHAP");
            RaisePropertyChanged("WHOHAZ");
            RaisePropertyChanged("WHOWAP");
            RaisePropertyChanged("WHOWAZ");
            RaisePropertyChanged("WHOWHP");
            RaisePropertyChanged("WHOWHZ");
            RaisePropertyChanged("WHOHCP");
            RaisePropertyChanged("WHOHCZ");

            RaisePropertyChanged("WHOSSFZ");
            RaisePropertyChanged("WHOSSFP");
            RaisePropertyChanged("WHOTSFZ");
            RaisePropertyChanged("WHOTSFP");
            RaisePropertyChanged("WHOMUACZ");
            RaisePropertyChanged("WHOMUACP");

            RaisePropertyChanged("BodyMassIndex");
        }
        #endregion // Methods
    }
}
